/*
 * JScroll - the scrollable desktop pane for Java.
 * Copyright (C) 2003 Tom Tessier
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

package org.jscroll.components;

import javax.swing.*;

import java.awt.*;
import java.awt.event.*;

import java.util.*;


/**
 * Generic self-contained resizable toolbar class. When a button addition exceeds
 * the width of the toolbar container, all buttons within the container are
 * automatically resized to compensate, down to the minimum button width defined
 * upon creation of the ResizableToolbar instance.
 *
 * @author <a href="mailto:tessier@gabinternet.com">Tom Tessier</a>
 * @version 1.0  03-Mar-2001
 */
public class ResizableToolBar extends JToolBar implements ComponentListener {
    // ButtonGroups for toolbar buttons
    private ButtonGroup buttonGroup;
    private int minButtonWidth;
    private int maxButtonWidth;

    /**
     * creates the ResizableToolbar object
     *
     * @param minButtonWidth the minimum button width allowed
     * @param maxButtonWidth the maximum button width allowed
     */
    public ResizableToolBar(int minButtonWidth, int maxButtonWidth) {
        buttonGroup = new ButtonGroup();
        setFloatable(false);
        this.minButtonWidth = minButtonWidth;
        this.maxButtonWidth = maxButtonWidth;

        addComponentListener(this);
    }

    /**
     * adds a button to the ResizableToolbar
     *
     * @param button the button to add
     */
    public void add(AbstractButton button) {
        buttonGroup.add(button);
        super.add(button);
        button.setSelected(true);
        resizeButtons();
    }

    /**
     * removes a button from the ResizableToolbar
     *
     * @param button the button to remove
     */
    public void remove(AbstractButton button) {
        super.remove(button);
        buttonGroup.remove(button);
        resizeButtons();
        repaint();
    }

    /**
     * returns the ResizableToolbar elements
     *
     * @return an Enumeration of the ResizableToolbar elements
     */
    public Enumeration getElements() {
        return buttonGroup.getElements();
    }

    /**
     * returns the number of buttons stored within the ResizableToolbar
     *
     * @return the number of buttons
     */
    public int getButtonCount() {
        // note: getButtonCount() will not work with JDK 1.2
        return buttonGroup.getButtonCount();
    }

    /**
     * resizes the buttons of the toolbar, depending upon the total number
     * of components stored therein.
     * Executes as an "invoked later" thread for a slight perceived
     * performance boost.
     */
    private void resizeButtons() {
        final float exactButtonWidth = getCurrentButtonWidth();

        SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    JToggleButton b = null;
                    Enumeration e = getElements();

                    float currentButtonXLocation = 0.0f;

                    // resize the buttons
                    while (e.hasMoreElements()) {
                        b = (JToggleButton) e.nextElement();

                        int buttonWidth = Math.round(currentButtonXLocation +
                                exactButtonWidth) -
                            Math.round(currentButtonXLocation);
                        assignWidth(b, buttonWidth);

                        currentButtonXLocation += exactButtonWidth;
                    }

                    revalidate();
                }
            });
    }

    /**
     * returns the current button width, defined as the width of the ResizableToolbar
     *      divided by the number of buttons. The value returned ranges from
     *      minButtonWidth to maxButtonWidth (two variables defined upon creation
     *      of the ResizableToolbar instance).
     *
     * @return the current button width as a float.
     */
    private float getCurrentButtonWidth() {
        int width = getWidth() - getInsets().left - getInsets().right;

        // if width <= 0, means JToolbar hasn't been displayed yet, so use
        // the maximum button width
        float buttonWidth = ((width <= 0) ? maxButtonWidth : width);

        int numButtons = getButtonCount();

        // have at least one button? then divide the width by the # of buttons
        // (ie: resultant buttonWidth = viewport width / # of buttons)
        if (numButtons > 0) {
            buttonWidth /= numButtons;
        }

        if (buttonWidth < minButtonWidth) {
            buttonWidth = minButtonWidth;
        } else if (buttonWidth > maxButtonWidth) {
            buttonWidth = maxButtonWidth;
        }

        return buttonWidth;
    }

    /**
     * assigns a new width to the specified button
     *
     * @param b the button whose width is to be adjusted
     * @param buttonWidth the new width
     */
    private void assignWidth(JToggleButton b, int buttonWidth) {
        b.setMinimumSize(new Dimension(buttonWidth - 2,
                b.getPreferredSize().height));
        b.setPreferredSize(new Dimension(buttonWidth,
                b.getPreferredSize().height));

        Dimension newSize = b.getPreferredSize();
        b.setMaximumSize(newSize);
        b.setSize(newSize);
    }

    /////
    // respond to resize events...
    /////

    /**
     * resize the buttons when the ResizableToolbar itself is resized
     *
     * @param e the ComponentEvent
     */
    public void componentResized(ComponentEvent e) {
        resizeButtons();
    }

    /**
     * interface placeholder
     *
     * @param e the ComponentEvent
     */
    public void componentShown(ComponentEvent e) {
    }

    /**
     * interface placeholder
     *
     * @param e the ComponentEvent
     */
    public void componentMoved(ComponentEvent e) {
    }

    /**
     * interface placeholder
     *
     * @param e the ComponentEvent
     */
    public void componentHidden(ComponentEvent e) {
    }
}
